﻿using System.Collections.Generic;
using UnityEngine.UIElements;

namespace winS.UnityEditor.UIElement
{
    /// <summary>
    /// Element container that can slide vertically.
    /// Each element can have a different height.
    /// </summary>
    public class VerticalSlideView : VisualElement
    {
        public static readonly float defaultSlideDuration = 0.24f;

        public int index { get; private set; }
        public float slideDuration { get => _slideDuration; set => SetSlideDuration(value); }

        private readonly VisualElement mask;
        private readonly VisualElement itemList;
        private readonly List<ItemHeightInfo> itemHeightInfoList = new List<ItemHeightInfo>();

        private float _slideDuration;

        internal VerticalSlideView()
        {
            style.overflow = Overflow.Hidden;
            base.Add(mask = new VisualElement());
            mask.style.overflow = Overflow.Hidden;
            mask.style.SetHeight(100f, LengthUnit.Percent);
            mask.Add(itemList = new VisualElement());
            itemList.style.SetHeight(100f, LengthUnit.Percent);

            mask.style.flexGrow = itemList.style.flexGrow = 1f;

            SetSlideDuration(defaultSlideDuration);

            RegisterCallback<GeometryChangedEvent>(GeometryChangedCallback);
        }

        public new void Clear()
        {
            itemList.Clear();
        }
        public new void Add(VisualElement content)
        {
            VisualElement item = new VisualElement();
            item.style.flexBasis = 0f;
            item.style.flexShrink = 0f;
            item.style.SetMinHeight(100f, LengthUnit.Percent);
            item.Add(content);
            itemList.Add(item);
        }
        public void SlideContent(int index, bool animation = true)
        {
            this.index = index;
            if (panel == null) return;
            if (animation) itemList.style.top = -itemHeightInfoList[index].top;
            else RebuildLayout();
        }
        public void RebuildLayout()
        {
            style.transitionProperty = mask.style.transitionProperty = itemList.style.transitionProperty = EditorElementUtility.emptyPropertyList;

            itemList.style.top = -itemHeightInfoList[index].top;

            style.transitionProperty = mask.style.transitionProperty = EditorElementUtility.heightPropertyList;
            itemList.style.transitionProperty = EditorElementUtility.topPropertyList;
        }

        private void SetSlideDuration(float newValue)
        {
            _slideDuration = newValue;
            style.transitionProperty = mask.style.transitionProperty = EditorElementUtility.heightPropertyList;
            itemList.style.transitionProperty = EditorElementUtility.topPropertyList;

            style.transitionDuration = mask.style.transitionDuration = itemList.style.transitionDuration = new List<TimeValue>() { _slideDuration };
        }
        private void GeometryChangedCallback(GeometryChangedEvent geometryChangedEvent)
        {
            itemHeightInfoList.Clear();
            float currentTop = 0f;
            foreach (var item in itemList.Children())
            {
                float height = item.layout.height;
                itemHeightInfoList.Add(new ItemHeightInfo(currentTop, height));
                currentTop += height;
            }
            RebuildLayout();
        }

        private readonly struct ItemHeightInfo
        {
            public readonly float top;
            public readonly float height;

            public ItemHeightInfo(float top, float height)
            {
                this.top = top;
                this.height = height;
            }
        }
    }
}